package com.yahoo.dtf.javadoc;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.OutputStream;
import java.io.PrintStream;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map.Entry;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Source;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import javax.xml.transform.stream.StreamSource;
import org.w3c.dom.Document;
import com.sun.javadoc.ClassDoc;
import com.sun.javadoc.DocErrorReporter;
import com.sun.javadoc.FieldDoc;
import com.sun.javadoc.MethodDoc;
import com.sun.javadoc.RootDoc;
import com.sun.javadoc.Tag;
import com.yahoo.dtf.exception.DTFException;
import com.yahoo.dtf.util.StringUtil;
import com.yahoo.dtf.xml.XSDHandler;
/**
*
* @dtf.feature XML Documentation
* @dtf.feature.group Tag Development
* @dtf.feature.desc
*
* <p>
* When writing tests you can document those tests using JavaDoc tags, that are
* then transformed into this document you are reading right now. Using these
* tags is a really simple and you only need to know some HTML if you want to
* make your documentation look a bit neater. Here are the list of available
* documentation tags that you can use within your tags and also to document any
* new features built into DTF.
* </p>
* <p>
* For examples on using these tags just have a look at any source file with
* '@dtf.feature' or '@dtf.tag' taglets in them and you'll find various examples
* on how to use this feature to better the documentation of your code.
* </p>
*
* <h3>Tag Documentation</h3>
* <table border="1" width="75%">
* <tr><th colspan="2">Class Taglets</th></tr>
* <tr><th width="200px">Taglet</th><th>Description</th></tr>
* <tr>
* <td><b>@dtf.tag tag</b></td>
* <td>This tag is required to identify that the following
* documentation is for a tag named tag.
* </td>
* </tr>
* <tr>
* <td><b>@dtf.tag.desc desc</b></td>
* <td>This is where you can place a detailed description of what this
* tag does and what other resources are useful for reading before
* using this tag. You can place almost any HTML elements in this
* section which allows you to format your documentation nicely
* using tables and lists.
* </td>
* </tr>
* <tr>
* <td><b>@dtf.author author</b></td>
* <td>Here you can put the author or authors names who are responsible
* for maintaining this tag.</td>
* </tr>
* <tr>
* <td><b>@dtf.since version</b></td>
* <td>Just a simple indication of since when this tag has existed in
* terms of versions of DTF.</td>
* </tr>
* <tr>
* <td><b>@dtf.event name</b></td>
* <td>This taglet is used to document exactly what events are being
* thrown by the tag being documented. This makes the life of the
* test writer really simple because he/she can consult this
* documentation to easily identify what information is available
* from executing this tag.
* </td>
* </tr>
* <tr>
* <td><b>@dtf.event.attr name</b></td>
* <td>With this taglet you can identify all the names of attributes
* that an event has so that the test writer knows exactly what
* he/she can get back from executing your tag. This tag depends on
* the previously documented event name so be sure to use the
* '@dtf.event' taglet before this one.</td>
* </tr>
* <tr>
* <td><b>@dtf.event.attr.desc desc</b></td>
* <td>This taglet is used to describe the previously identified
* attribute using the @dtf.event.attr taglet. So be sure to use
* that taglet previous to this one otherwise logically the
* documentation won't make sense.
* </td>
* </tr>
* <tr>
* <td><b>@dtf.example example</b></td>
* <td>This tag can be used to identify example XML documentation that
* shows exact XML samples of how to use the tag being documented.
* So the body of this taglet can be any XML you'd like just be
* sure to place a single root element around your XML.
* </td>
* </tr>
* </table>
* <br/>
*
* <table border="1" width="75%">
* <tr><th colspan="2">Attribute Taglets</th></tr>
* <tr><th width="200px">Taglet</th><th>Description</th></tr>
* <tr>
* <td><b>@dtf.attr name</b></td>
* <td>This identifies the name of the attribute being documented here.</td>
* </tr>
* <tr>
* <td><b>@dtf.attr.desc desc</b></td>
* <td>
* This is where you can place the description of the attribute being
* documented.You can place almost any HTML elements in this section
* which allows you to format your documentation nicely using tables
* and lists.
* </td>
* </tr>
* </table>
* <br/>
*
* <table border="1" width="75%">
* <tr><th colspan="2">Utility Taglets</th></tr>
* <tr><th width="200px">Taglet</th><th>Description</th></tr>
* <tr>
* <td><b>@dtf.link name</b></td>
* <td>
* This tag allows you to create a link to another tag/feature just by
* having the correct name specified. The name is case insensitive so
* that you don't have to know the exact case spelling of the tag or
* feature you're trying to link to.
* </td>
* </tr>
* </table>
* <br/>
*
* <h2>Feature Documentation</h2>
* <p>
* The feature documentation allows the developer to document features that may
* not be a simple XML tag and instead could be the documentation of how
* certain dynamic properties work or the documentation on how ranges work.
* </p>
* <p>
* The only two things you need to define for a feature is what the name of
* the feature is and which feature group it belongs to. The name of the feature
* will appear as a link underneat the feature group. There are some already
* existing groups that you can attach your documentation to or you can build
* your own group name and that will be added to the list of documented
* features in the generated feature documentation.
* </p>
* <table border="1" width="75%">
* <tr><th colspan="2">Feature Taglets</th></tr>
* <tr><th width="200px">Taglet</th><th>Description</th></tr>
* <tr>
* <td><b>@dtf.feature name</b></td>
* <td>The name of the feature being documented.</td>
* </tr>
* <tr>
* <td><b>@dtf.feature.group group</b></td>
* <td>
* The logical group belongs to within the documentation being
* generated. This is going to be used to display this feature within
* the same group as as other features with the same group name.
* </td>
* </tr>
* <tr>
* <td><b>@dtf.feature.desc desc</b></td>
* <td>
* The description of this feature along with any information that
* might be useful for the test writers that may find this feature to
* be useful. You can place almost any HTML elements in this section
* which allows you to format your documentation nicely using tables
* and lists.
* </td>
* </tr>
* <tr>
* <td><b>@dtf.example example</b></td>
* <td>
* This tag can be used to identify example XML documentation that
* shows exact XML samples of how to use the feature being documented.
* So the body of this taglet can be any XML you'd like just be sure to
* place a single root element around your XML.
* </td>
* </tr>
* </table>
*/
public class DTFDoclet {
private static final String TAGS_DIRECOTRY = "tags";
private static final String FEATURES_DIRECTORY = "features";
public static final String DTF_TAG = "dtf.tag";
public static final String DTF_AUTHOR = "dtf.author";
public static final String DTF_SINCE = "dtf.since";
public static final String DTF_TAG_DESC = "dtf.tag.desc";
public static final String DTF_TAG_EXAMPLE = "dtf.tag.example";
public static final String DTF_ATTR = "dtf.attr";
public static final String DTF_ATTR_DESC = "dtf.attr.desc";
public static final String DTF_LINK = "dtf.link";
public static final String DTF_XML = "dtf.xml";
public static final String DTF_EVENT = "dtf.event";
public static final String DTF_EVENT_ATTR = "dtf.event.attr";
public static final String DTF_EVENT_ATTR_DESC = "dtf.event.attr.desc";
public static final String DTF_FEATURE = "dtf.feature";
public static final String DTF_FEATURE_GROUP = "dtf.feature.group";
public static final String DTF_FEATURE_DESC = "dtf.feature.desc";
public static final String DTF_EXAMPLE = "dtf.example";
/*
* Used to basically flag that a specific tag should not be added to the
* main page indexed tags. This is useful if you have tags that only apply
* within certain other tags and shouldn't be seen as root level tags.
*/
public static final String DTF_SKIP_INDEX = "dtf.skip.index";
private static class DTFDoc {
public String name = null;
public Tag[] descriptions = null;
public Tag[] examples = null;
public String qname = null;
}
public static boolean start(RootDoc root) throws Exception {
ClassDoc[] classes = root.classes();
ArrayList<String> packagenames = new ArrayList<String>();
HashMap<String, ArrayList<ClassDoc>> packages =
new HashMap<String, ArrayList<ClassDoc>>();
ArrayList<String> fgroupnames = new ArrayList<String>();
HashMap<String, ArrayList<DTFDoc>> fgroups =
new HashMap<String, ArrayList<DTFDoc>>();
String[][] options = root.options();
String destination = null;
String xsd = null;
for(int i = 0; i < options.length; i++) {
if (options[i][0].equals("-d")) {
destination = options[i][1];
}
if (options[i][0].equals("-dtfxsd")) {
xsd = options[i][1];
}
}
if (destination == null) {
throw new IllegalArgumentException("-d option needs to be specified");
}
if (xsd == null) {
throw new IllegalArgumentException("-dtfxsd option needs to be specified");
}
String tagdir = destination + File.separatorChar + TAGS_DIRECOTRY;
File dir = new File(tagdir);
if ( !dir.exists() && !dir.mkdirs() ) {
throw new DTFException("Unable to create directory [" + tagdir + "]");
}
String featuredir = destination + File.separatorChar + FEATURES_DIRECTORY;
dir = new File(featuredir);
if ( !dir.exists() && !dir.mkdirs() ) {
throw new DTFException("Unable to create directory [" + featuredir + "]");
}
PrintStream psIndex = createFile(destination + File.separatorChar + "index.html");
psIndex.println("<html>");
psIndex.println(" <head>");
psIndex.println(" <link rel='stylesheet' " +
"href='main.css' " +
"type='text/css'>");
psIndex.println(" </head>");
psIndex.println(" <body id='body'>");
psIndex.println(" <h1>DTF Documentation</h1>");
ArrayList<String> tnames = new ArrayList<String>();
ArrayList<String> fnames = new ArrayList<String>();
/*
* 1st phase
*
* Filter out non DTF related JavaDocs and also gather the list of
* packages that contain different DTF Actions. Create a TOC for the
* whole document.
*/
for (int i = 0; i < classes.length; ++i) {
ClassDoc classdoc = classes[i];
/*
* Identify which ones have DTF XML tags
*/
if (classdoc.tags(DTF_TAG).length != 0) {
String classname = classdoc.qualifiedName();
String packagename = classname.substring(0, classname.lastIndexOf("."));
tnames.add(classname.replace(packagename + ".", "").toLowerCase());
if (!packages.containsKey(packagename)) {
packages.put(packagename, new ArrayList<ClassDoc>());
packagenames.add(packagename);
}
packages.get(packagename).add(classdoc);
}
MethodDoc[] methoddocs = classes[i].methods();
for (int m = 0; m < methoddocs.length; m++ ) {
if ( methoddocs[m].tags(DTF_FEATURE).length != 0 ) {
String fgroup = methoddocs[m].tags(DTF_FEATURE_GROUP)[0].text();
if (!fgroups.containsKey(fgroup)) {
fgroups.put(fgroup, new ArrayList<DTFDoc>());
fgroupnames.add(fgroup);
}
DTFDoc dtfdoc = new DTFDoc();
dtfdoc.name = methoddocs[m].tags(DTF_FEATURE)[0].text();
dtfdoc.descriptions = methoddocs[m].tags(DTF_FEATURE_DESC);
dtfdoc.examples = methoddocs[m].tags(DTF_EXAMPLE);
fnames.add(dtfdoc.name.trim().toLowerCase());
fgroups.get(fgroup).add(dtfdoc);
}
}
/*
* Find the documented features through out the code as well
*/
if (classdoc.tags(DTF_FEATURE).length != 0) {
String fgroup = classdoc.tags(DTF_FEATURE_GROUP)[0].text();
if (!fgroups.containsKey(fgroup)) {
fgroups.put(fgroup, new ArrayList<DTFDoc>());
fgroupnames.add(fgroup);
}
DTFDoc dtfdoc = new DTFDoc();
dtfdoc.name = classdoc.tags(DTF_FEATURE)[0].text();
dtfdoc.descriptions = classdoc.tags(DTF_FEATURE_DESC);
dtfdoc.examples = classdoc.tags(DTF_EXAMPLE);
dtfdoc.qname = classdoc.qualifiedName();
fnames.add(dtfdoc.name.trim().toLowerCase());
fgroups.get(fgroup).add(dtfdoc);
}
}
/*
* Sorting makes it easier to find what you want in the documentation
*/
Collections.sort(packagenames);
Collections.sort(fgroupnames);
Iterator<String> iter = fgroupnames.iterator();
psIndex.println("<div id='features'>");
psIndex.println(" <div id='features_title'>Features</div>");
int counttags = 0;
while (iter.hasNext()) {
if ( counttags == 0 ) {
psIndex.println("<div class='feature_row'>");
}
psIndex.println("<div class='feature_div'>");
String feature = (String) iter.next();
psIndex.println("<div class='feature_name'>" +
feature + "</div>");
psIndex.println("<div id='collapsible_"+ feature +
"' class='feature_elems'" +
"style='display: block;'>");
ArrayList<DTFDoc> features = fgroups.get(feature);
for (int i = 0; i < features.size(); i++) {
DTFDoc dtfdoc = features.get(i);
psIndex.println("<a class='feature_elem' href='" + FEATURES_DIRECTORY + "/" +
dtfdoc.name.toLowerCase() + ".html'>" + dtfdoc.name +
"</a>");
}
psIndex.println("</div>");
psIndex.println("</div>");
counttags++;
if ( counttags == 4 ) {
psIndex.println("</div>");
counttags = 0;
}
}
if ( counttags > 0 ) {
psIndex.println("</div>");
}
psIndex.println("</div>");
/*
* Link to the root element of the DTF XML as a starting point for the
* testcase writing.
*/
Collections.sort(packagenames);
iter = packagenames.iterator();
psIndex.println("<div id='tags'>");
psIndex.println("<div id='tags_title'>Tags</div>");
counttags = 0;
while (iter.hasNext()) {
String pname = (String) iter.next();
ArrayList<ClassDoc> tags = packages.get(pname);
String aux = pname.substring(pname.lastIndexOf(".actions.") +
"actions.".length()+1);
aux = aux.replaceAll("\\."," ");
aux = StringUtil.capitalize(aux);
StringBuffer buffer = new StringBuffer();
for (int i = 0; i < tags.size(); i++) {
ClassDoc classdoc = (ClassDoc)tags.get(i);
if ( classdoc.tags(DTF_SKIP_INDEX).length == 0 ) {
String tagname = classdoc.name().toLowerCase();
buffer.append("<a class='tag_elem' href='" +
TAGS_DIRECOTRY + "/" + tagname + ".html'>" +
tagname + "</a> ");
}
}
if ( buffer.length() != 0 ) {
if ( counttags == 0 ) {
psIndex.println("<div class='tag_row'>");
}
psIndex.println("<div class='tag_div'>");
psIndex.println("<div class='tag_name' " +
"onclick=\"togglediv('collapsible_" +
aux + "');\">" +
aux + "</div>");
psIndex.println("<div id='collapsible_" + aux + "' " +
"class='tag_elems' " +
"style='display: block;'>");
psIndex.print(buffer.toString());
psIndex.println("</div>");
psIndex.println("</div>");
counttags++;
if ( counttags == 5 ) {
psIndex.println("</div>");
counttags = 0;
}
}
}
if ( counttags > 0 ) {
psIndex.println("</div>");
}
psIndex.println("</div>");
/*
* 1st phase
*
* Tag documentation generation that will generate a separate HTML file
* for each of the DTF actions that are documented with DTF javadoc
* tags.
*/
iter = packagenames.iterator();
FileInputStream fis;
try {
fis = new FileInputStream(xsd);
} catch (FileNotFoundException e) {
throw new Exception("Error accessing XSD.",e);
}
XSDHandler dtfXSD = new XSDHandler(fis);
while (iter.hasNext()) {
String pname = (String) iter.next();
ArrayList tags = (ArrayList)packages.get(pname);
info("Package " + pname);
for (int i = 0; i < tags.size(); i++) {
ClassDoc classdoc = (ClassDoc)tags.get(i);
String tagname = classdoc.name().toLowerCase();
PrintStream ps = createFile(tagdir + File.separatorChar +
tagname + ".html");
info("Tag " + tagname);
ps.println("<html>");
ps.println(" <head>");
ps.println(" <link rel='stylesheet' " +
"href='../main.css' " +
"type='text/css'>");
ps.println(" <title>" + classdoc.name() + "</title>");
ps.println(" </head>");
ps.println(" <body id='body'>");
ps.println("<div id='nav'>");
ps.println("<a href='../index.html'>Home</a></div>");
// Hiding the class name in the tooltip of the title :)
ps.println("<div id='tag_title' title='" + classdoc.qualifiedName() +
"'>" + classdoc.name() + "</div>");
/*
* Description
*/
ps.println("<div id='tag_description'>");
Tag[] descriptions = classdoc.tags(DTF_TAG_DESC);
if (descriptions.length != 0) {
for (int d = 0; d < descriptions.length; d++) {
ps.print(treatTag(descriptions[d],tnames,fnames));
}
}
ps.println("</div>");
/*
* Child tags
*/
String children = dtfXSD.generateChildrenString(tagname);
if ( children != null && children.trim().length() > 0 ) {
Pattern pattern = Pattern.compile("([a-zA-Z_]*)");
Matcher matcher = pattern.matcher(children);
StringBuffer result = new StringBuffer();
int laststart = 0;
while ( matcher.find() ) {
String match = matcher.group();
int start = matcher.start();
int end = matcher.end();
match = match.toLowerCase();
if ( match.trim().length() > 0 ) {
result.append(children.substring(laststart,start));
result.append("<a href='" + match + ".html'>" + match + "</a>");
laststart=end;
}
}
result.append(children.substring(laststart));
ps.print("<div class='tag_children_label'>Children Tags</div>");
ps.print("<div class='tag_children'>");
ps.print(result.toString());
ps.print("</div>");
}
/*
* Events
*/
Tag[] events = classdoc.tags(DTF_EVENT);
Tag[] eventsAttrs = classdoc.tags(DTF_EVENT_ATTR);
Tag[] eventsDesc= classdoc.tags(DTF_EVENT_ATTR_DESC);
if (eventsAttrs.length != eventsDesc.length) {
// XXX: throw an exception
}
if (eventsAttrs.length != 0) {
HashMap<String, HashMap<String, String>> eventMap =
new HashMap<String, HashMap<String,String>>();
/*
* Construct all the relations between events and their
* attributes.
*/
for (int a = 0; a < events.length; a++) {
String[] eventNames = events[a].text().trim().split(" ");
for (int e = 0; e < eventNames.length; e++) {
if (!eventMap.containsKey(eventNames[e])) {
eventMap.put(eventNames[e], new HashMap<String,String>());
}
String attrName = eventsAttrs[a].text();
String attrDesc = treatTag(eventsDesc[a],tnames,fnames);
((HashMap)eventMap.get(eventNames[e])).put(attrName, attrDesc);
}
}
ps.print("<div class='tag_events_label'>Events</div>");
Iterator<Entry<String,HashMap<String,String>>> eventIter =
eventMap.entrySet().iterator();
ps.print("<div class='tag_events'>");
while ( eventIter.hasNext() ) {
Entry<String,HashMap<String, String>> entry =
eventIter.next();
HashMap<String, String> attribs = entry.getValue();
ps.print("<div class='tag_event'>");
ps.print("<div class='tag_event_name'>" + entry.getKey()
+ " Event</div>");
Iterator<Entry<String,String>> attribIter = attribs.entrySet().iterator();
while ( attribIter.hasNext() ) {
Entry<String,String> attrib = attribIter.next();
ps.print("<div class='tag_event_attribute'>");
ps.print("<div class='tag_event_attribute_name'>" +
attrib.getKey() + "</div>");
ps.print("<div class='tag_event_attribute_value'>" +
attrib.getValue() + "</div>");
ps.print("</div>");
}
ps.print("</div>");
}
ps.print("</div>");
}
/*
* Attributes
*/
HashMap attrs = new HashMap();
ClassDoc aux = classdoc;
while (aux != null) {
FieldDoc[] fieldDocs = aux.fields();
for (int f = 0; f < fieldDocs.length; f++) {
attrs.put(fieldDocs[f].name(), fieldDocs[f]);
}
aux = aux.superclass();
}
ArrayList<String> attributes = dtfXSD.getAttributes(tagname);
ArrayList<String> required = dtfXSD.getRequiredAttributes(tagname);
ArrayList<String> optional = dtfXSD.getOptionalAttributes(tagname);
if ( attributes != null ) {
boolean someopt = false;
StringBuffer opt = new StringBuffer();
boolean somereq = false;
StringBuffer req = new StringBuffer();
opt.append("<div id='optional_attributes_label'>Optional Attributes</div>");
opt.append("<div id='optional_attributes'>");
req.append("<div id='required_attributes_label'>Required Attributes</div>");
req.append("<div id='required_attributes'>");
Iterator<String> keys = attributes.iterator();
while (keys.hasNext()) {
String aName = keys.next();
StringBuffer which = null;
if ( required.contains(aName) ) {
which = req;
somereq = true;
} else if ( optional.contains(aName) ) {
which = opt;
someopt = true;
}
if ( which != null ) {
which.append("<div class='attribute'>");
which.append("<div class='attribute_name'>" + aName + "</div>");
FieldDoc doc = (FieldDoc)attrs.get(aName);
if (doc != null) {
Tag[] descs = doc.tags(DTF_ATTR_DESC);
if (descs.length != 0) {
which.append("<div class='attribute_desc'>" +
treatTag(descs[0],tnames,fnames) +
"</div>");
}
}
which.append("</div>");
}
}
if (somereq) {
ps.print(req + "</div>");
}
if (someopt) {
ps.print(opt + "</div>");
}
}
/*
* Examples
*/
Tag[] examples = classdoc.tags(DTF_TAG_EXAMPLE);
if (examples.length != 0) {
ps.print("<div class='tag_examples_label'>Usage Examples</div>");
ps.print("<div class='tag_examples'>");
for (int e = 0; e < examples.length; e++) {
Tag example = examples[e];
try {
String text = example.text().trim();
if (text.length() != 0) {
ps.print("<div class='tag_example_container'>");
ps.print("<div class='tag_example_title'>Example #" + (e+1) + "</div>");
ps.print("<div class='tag_example'>" + treatXML(text) + "</div>");
ps.print("</div>");
}
} catch (Exception exc) {
throw new Exception("Error handling example #" +
(e+1) + " of tag " + tagname, exc);
}
}
ps.print("</div>");
}
ps.print("</body></html>");
ps.close();
}
}
/*
* 2nd phase for Features
*
*/
iter = fgroupnames.iterator();
while (iter.hasNext()) {
String pname = (String) iter.next();
ArrayList<DTFDoc> tags = fgroups.get(pname);
info(pname);
for (int i = 0; i < tags.size(); i++) {
DTFDoc dtfdoc = tags.get(i);
PrintStream ps = createFile(featuredir +
File.separatorChar +
dtfdoc.name.toLowerCase() +
".html");
info("Feature [" + dtfdoc.name + "]");
ps.println("<html>");
ps.println(" <head>");
ps.println(" <link rel='stylesheet' " +
"href='../main.css' " +
"type='text/css'>");
ps.println(" <title>" + dtfdoc.name + "</title>");
ps.println(" </head>");
ps.println(" <body id='body'>");
ps.println("<div id='nav'>");
ps.println("<a href='../index.html'>Home</a></div>");
// Hiding the class name in the tooltip of the title :)
ps.println("<div class='feature_title' title='" + dtfdoc.qname
+ "'>" + dtfdoc.name + "</div>");
/*
* Description
*/
ps.print("<div class='feature_description'>");
Tag[] descriptions = dtfdoc.descriptions;
if (descriptions.length != 0) {
for (int d = 0; d < descriptions.length; d++) {
ps.print(treatTag(descriptions[d],tnames,fnames));
}
}
ps.print("</div>");
/*
* Examples
*/
Tag[] examples = dtfdoc.examples;
if (examples.length != 0) {
ps.print("<div class='feature_examples_label'>Usage Examples</div>");
ps.print("<div class='feature_examples'>");
for (int e = 0; e < examples.length; e++) {
Tag example = examples[e];
try {
String text = example.text().trim();
if (text.length() != 0) {
ps.print("<div class='feature_example_container'>");
ps.print("<div class='feature_example_title'>Example #" + (e+1) + "</div>");
ps.print("<div class='feature_example'>" + treatXML(text) + "</div>");
ps.print("</div>");
}
} catch (Exception exc) {
throw new Exception("Error handling example #" +
(e+1) + " of tag " + dtfdoc.name,
exc);
}
}
ps.print("</div>");
}
ps.print("</body></html>");
ps.close();
}
}
psIndex.print("</td></tr></table>");
psIndex.print("</body></html>");
psIndex.close();
return true;
}
public static PrintStream createFile(String filename) throws Exception {
FileOutputStream fos;
try {
fos = new FileOutputStream(filename);
} catch (FileNotFoundException e) {
throw new Exception("Unable to create destination folders.",e);
}
return new PrintStream(fos);
}
public static String treatTag(Tag tag,
ArrayList<String> tnames,
ArrayList<String> fnames) throws Exception {
StringBuffer result = new StringBuffer();
Tag[] tags = tag.inlineTags();
for (int i = 0; i < tags.length; i++) {
Tag aux = tags[i];
if (aux.name().equalsIgnoreCase("text")) {
result.append(aux.text());
} else if (aux.name().equals("@"+DTF_LINK)) {
String name = aux.text().trim().toLowerCase();
String loc = null;
if ( tnames.contains(name) ) {
loc = "tags";
} else if ( fnames.contains(name) ) {
loc = "features";
} else {
throw new Exception("Unable to find link to [" +
name + "].");
}
result.append("<a href='../" + loc + "/" + name +
".html'>" + aux.text() + "</a>");
} else if (aux.name().equals("@"+DTF_XML)) {
result.append(treatXML(aux.text()));
}
}
return result.toString();
}
public static String treatXML(String string) throws Exception {
try {
DocumentBuilderFactory docBuilderFactory = DocumentBuilderFactory.newInstance();
DocumentBuilder docBuilder = docBuilderFactory.newDocumentBuilder();
string = string.replaceFirst("\\<\\?xml[^<^>]*\\?\\>","");
ByteArrayInputStream bais =
new ByteArrayInputStream(string.getBytes());
Document doc = docBuilder.parse(bais);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
serialize(doc,baos);
return "<div class='xml_code'>" + baos.toString() + "</div>";
} catch (Exception e) {
throw new Exception("Error processing XML node.",e);
}
}
public static void serialize(Document doc, OutputStream out) throws Exception {
TransformerFactory tfactory = TransformerFactory.newInstance();
Transformer serializer;
try {
ByteArrayOutputStream aux = new ByteArrayOutputStream();
// pretty print the XML
FileInputStream fis = new FileInputStream("src/xsl/pretty_printing.xsl");
Source xsl = new StreamSource(fis);
serializer = tfactory.newTransformer(xsl);
serializer.transform(new DOMSource(doc), new StreamResult(aux));
DocumentBuilderFactory docBuilderFactory = DocumentBuilderFactory.newInstance();
DocumentBuilder docBuilder = docBuilderFactory.newDocumentBuilder();
ByteArrayInputStream bais = new ByteArrayInputStream(aux.toByteArray());
doc = docBuilder.parse(bais);
// XML to HTML
fis = new FileInputStream("src/xsl/xmlverbatim.xsl");
xsl = new StreamSource(fis);
serializer = tfactory.newTransformer(xsl);
serializer.transform(new DOMSource(doc), new StreamResult(out));
} catch (TransformerException e) {
throw new Exception("Error processing XML node.",e);
}
}
public static int optionLength(String option) {
if (option.equals("-d"))
return 2;
if (option.equals("-dtfxsd"))
return 2;
return 0;
}
public static boolean validOptions(String options[][],
DocErrorReporter reporter) {
return true;
}
public static void info(String message) {
System.out.println(message);
}
public static void error(String message) {
System.err.println(message);
}
public static void warn(String message) {
System.out.println("WARN: " + message);
}
}